/** Generate Tone
 *
 * Created : July 8, 2014
 *
 * @author pquiring
 */

import java.io.*;

import javaforce.*;

public class GenTone extends javax.swing.JDialog {

  /**
   * Creates new form GenTone
   */
  public GenTone(java.awt.Frame parent, boolean modal, TrackPanel track) {
    super(parent, modal);
    initComponents();
    JFAWT.centerWindow(this);
    this.track = track;
    if (track.selectStart != track.selectStop) {
      timeField.setEnabled(false);
      double time = track.selectStop - track.selectStart;
      time /= track.rate;
      timeField.setText("" + time );
    }
  }

  /**
   * This method is called from within the constructor to initialize the form.
   * WARNING: Do NOT modify this code. The content of this method is always
   * regenerated by the Form Editor.
   */
  @SuppressWarnings("unchecked")
  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  private void initComponents() {

    apply = new javax.swing.JButton();
    cancel = new javax.swing.JButton();
    jLabel2 = new javax.swing.JLabel();
    jLabel1 = new javax.swing.JLabel();
    freqField = new javax.swing.JTextField();
    ampField = new javax.swing.JTextField();
    jLabel3 = new javax.swing.JLabel();
    timeField = new javax.swing.JTextField();

    setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
    setTitle("Resample");
    setResizable(false);

    apply.setText("Apply");
    apply.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        applyActionPerformed(evt);
      }
    });

    cancel.setText("Cancel");
    cancel.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        cancelActionPerformed(evt);
      }
    });

    jLabel2.setText("Frequency:");

    jLabel1.setText("Amplitude:");

    freqField.setText("440");

    ampField.setText("0.5");

    jLabel3.setText("Length:");

    timeField.setText("5.0");

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(layout.createSequentialGroup()
        .addContainerGap()
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
          .addGroup(layout.createSequentialGroup()
            .addGap(0, 132, Short.MAX_VALUE)
            .addComponent(cancel)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(apply))
          .addGroup(layout.createSequentialGroup()
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
              .addComponent(jLabel1)
              .addComponent(jLabel2)
              .addComponent(jLabel3))
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
              .addComponent(freqField)
              .addComponent(ampField)
              .addComponent(timeField))))
        .addContainerGap())
    );
    layout.setVerticalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
        .addContainerGap()
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(jLabel2)
          .addComponent(freqField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(jLabel1)
          .addComponent(ampField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(jLabel3)
          .addComponent(timeField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(apply)
          .addComponent(cancel))
        .addContainerGap())
    );

    pack();
  }// </editor-fold>//GEN-END:initComponents

  private void cancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelActionPerformed
    dispose();
  }//GEN-LAST:event_cancelActionPerformed

  private void applyActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_applyActionPerformed
    apply();
    dispose();
  }//GEN-LAST:event_applyActionPerformed

  // Variables declaration - do not modify//GEN-BEGIN:variables
  private javax.swing.JTextField ampField;
  private javax.swing.JButton apply;
  private javax.swing.JButton cancel;
  private javax.swing.JTextField freqField;
  private javax.swing.JLabel jLabel1;
  private javax.swing.JLabel jLabel2;
  private javax.swing.JLabel jLabel3;
  private javax.swing.JTextField timeField;
  // End of variables declaration//GEN-END:variables

  private TrackPanel track;

  private void apply() {
    int rate = track.rate;
    int freq = JF.atoi(freqField.getText());
    double amp = Double.parseDouble(ampField.getText());
    if (amp > 1.0) amp = 1.0;
    if (amp < 0.0) amp = 0.0;
    boolean replace = false;
    long length;
    if (track.selectStart != track.selectStop) {
      replace = true;
      track.createModifyUndo();
      length = track.selectStop - track.selectStart;
    } else {
      length = (long)(Double.parseDouble(timeField.getText()) * rate);
    }
//    JFLog.log("length=" + length);
    String toPath = track.project.path + "/temp";
    new File(toPath).mkdir();
    FileOutputStream fos;
    long totalLength = length;
    switch (track.bits) {
      case 16: amp *= Short.MAX_VALUE; break;
      case 32: amp *= Integer.MAX_VALUE; break;
    }
    try {
      int cid[] = new int[track.channels];
      ByteArrayOutputStream baos[] = new ByteArrayOutputStream[track.channels];
      for(int ch=0;ch<track.channels;ch++) {
        baos[ch] = new ByteArrayOutputStream();
      }
      int size[] = new int[track.channels];
      double dSample = 0;
      double pos = 0, chpos = 0;
      double theta = 2.0 * Math.PI * freq / rate;
      while (length > 0) {
        int todo = track.maxChunkSize;
        if (todo > length) todo = (int)length;
        for(int ch=0;ch<track.channels;ch++) {
          chpos = pos;
          for(int a=0;a<todo;a++) {
            dSample = Math.sin(chpos) * amp;
            chpos += theta;
            switch (track.bits) {
              case 16: {
                short sample = (short) dSample;
                baos[ch].write(sample & 0xff);
                sample >>= 8;
                baos[ch].write(sample & 0xff);
                break;
              }
              case 32: {
                int sample = (int) dSample;
                baos[ch].write(sample & 0xff);
                sample >>= 8;
                baos[ch].write(sample & 0xff);
                sample >>= 8;
                baos[ch].write(sample & 0xff);
                sample >>= 8;
                baos[ch].write(sample & 0xff);
                break;
              }
            }
            size[ch]++;
            if (size[ch] == track.maxChunkSize) {
              fos = new FileOutputStream(toPath + "/c" + cid[ch] + "-" + ch + ".dat");
              TrackPanel.ChunkHeader chunk = new TrackPanel.ChunkHeader();
              chunk.cid = cid[ch]++;
              chunk.length = size[ch];
              chunk.next_cid = cid[ch];
              chunk.write(fos);
              fos.write(baos[ch].toByteArray());
              fos.close();
              size[ch] = 0;
              baos[ch].reset();
            }
          }
        }
        length -= todo;
        pos += theta * todo;
        int w = (int)pos;
        pos -= w;
      }
      for(int ch=0;ch<track.channels;ch++) {
        if (size[ch] > 0) {
          fos = new FileOutputStream(toPath + "/c" + cid[ch] + "-" + ch + ".dat");
          TrackPanel.ChunkHeader chunk = new TrackPanel.ChunkHeader();
          chunk.cid = cid[ch]++;
          chunk.length = size[ch];
          chunk.next_cid = 0;
          chunk.write(fos);
          fos.write(baos[ch].toByteArray());
          fos.close();
          size[ch] = 0;
          baos[ch].reset();
        } else {
          //need to patch last chunk (clear next_cid)
          RandomAccessFile raf = new RandomAccessFile(toPath + "/c" + (cid[ch]-1) + "-" + ch + ".dat", "rw");
          raf.seek(4);  //skip length
          raf.write(new byte[4]);  //write zero next_cid
          raf.close();
        }
      }
      //write clip header
      fos = new FileOutputStream(toPath + "/clip.dat");
      TrackPanel.ClipHeader clip = new TrackPanel.ClipHeader();
      clip.offset = 0;
      clip.length = totalLength;
      clip.tid = track.tid;
      clip.channels = track.channels;
      clip.bits = track.bits;
      clip.bytes = track.bytes;
      clip.rate = rate;
      clip.write(fos);
      fos.close();
    } catch (Exception e) {
      JFLog.log(e);
      dispose();
      return;
    }
    track.paste(toPath, replace);
    track.writeMainHeader();
    //now delete /temp contents
    File files[] = new File(toPath).listFiles();
    for(int a=0;a<files.length;a++) {
      files[a].delete();
    }
  }
}
